home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Tool Chest / Files / XTND 1.3.6 / Application Examples / PascalSource / THINK ƒ / XTEFileIO.p next >
Encoding:
Text File  |  1992-03-17  |  42.5 KB  |  1,373 lines  |  [TEXT/PJMM]

  1. {    File:        XTEFileIO.p}
  2. {}
  3. {    Contains:    Input/Output modules for use with XTND TEStyleSample}
  4. {}
  5. {    Copyright:    © 1991 by Claris Corporation, all rights reserved.}
  6. {}
  7.  
  8. UNIT XTEFileIO;
  9.  
  10. INTERFACE
  11.  
  12.     USES
  13.         types, osutils, files, folders, QuickDraw, Dialogs, Printing, Script, XTNDInterface, XTNDTextTranslator, XTNDPictTranslator;
  14.  
  15.     CONST
  16.         kNoText = '';
  17.         kTabChar = '\t';
  18.         kSpaceChar = ' ';
  19.         kReturnChar = '\n';
  20.         kRightBracket = ']';
  21.         kFormulaText = '[Formula]';
  22.         kNumberText = '[Number]';
  23.         kCitationText = '[Citation]';
  24.         kUnknownChar = '[Unknown Special Char';
  25.         kUnknownFrame = '[Unknown Frame]';
  26.         kMergeBreak = '\r[Merge Break]\r';
  27.         kColumnBreak = '\r[Column Break]\r';
  28.         kPageBreak = '\r[Page Break]\r';
  29.         kSegmentBreak = '\r[Segment Break]\r';
  30.         kSectionBreak = '\r[Section Break]\r';
  31.         kPageNumText = '[Page Number]';
  32.         kFootnoteText = '[Footnote #';
  33.         kPictureText = '[Picture #';
  34.  
  35.         kNativeTypes = 1;
  36.  
  37.     {kMaxDocWidth is an arbitrary number used to specify the width of the TERec's}
  38. {    destination rectangle so that word wrap and horizontal scrolling can be}
  39. {    demonstrated.}
  40.         kMaxDocWidth = 576;
  41.  
  42.     {kTextMargin is the number of pixels we leave blank at the edge of the window.}
  43.         kTextMargin = 2;
  44.  
  45.     {kScrollBarAdjust and kScrollBarWidth are used in calculating}
  46. {    values for control positioning and sizing.}
  47.         kScrollbarWidth = 16;
  48.         kScrollbarAdjust = kScrollbarWidth - 1;
  49.  
  50.     {kScrollTweek compensates for off-by-one requirements of the scrollbars}
  51. {     to have borders coincide with the growbox.}
  52.         kScrollTweek = 2;
  53.  
  54.     {kCrChar is used to match with a carriage return when calculating the}
  55. {    number of lines in the TextEdit record. kDelChar is used to check for}
  56. {    delete in keyDowns.}
  57.         kCRChar = 13;
  58.         kDelChar = 8;
  59.  
  60.     {kButtonScroll is how many pixels to scroll horizontally when the button part}
  61. {    of the horizontal scrollbar is pressed.}
  62.         kButtonScroll = 4;
  63.  
  64.     {kErrStrings is the resource ID for the error strings STR# resource.}
  65.         kErrStrings = 128;
  66.         kFileMessageID = 131;
  67.         kTextRunsDispID = 134;
  68.  
  69.     { The following constants are all resource IDs, corresponding to their resources }
  70.  
  71.         rMenuBar = 128;                { application's menu bar }
  72.         rAboutAlert = 128;                { about alert }
  73.         rDocWindow = 128;                { application's window }
  74.  
  75.         rVScroll = 128;                { vertical scrollbar control }
  76.         rHScroll = 129;                { horizontal scrollbar control }
  77.  
  78.         rUserAlert = 129;                { user error alert }
  79.  
  80.     { The following are indicies into STR# resources. }
  81.         eWrongMachine = 1;
  82.         eSmallSize = 2;
  83.         eNoMemory = 3;
  84.         eNoSpaceCut = 4;
  85.         eNoCut = 5;
  86.         eNoCopy = 6;
  87.         eExceedPaste = 7;
  88.         eNoSpacePaste = 8;
  89.         eNoWindow = 9;
  90.         eExceedChar = 10;
  91.         eNoPaste = 11;
  92.         eNoXTND = 12;
  93.         eTranslatorLoad = 13;
  94.         eImportOpenRes = 14;
  95.         eFilterRead = 15;
  96.         eImportOpen = 16;
  97.         eFilterInit = 17;
  98.         eDeleteFailed = 18;
  99.         eCreateFail = 19;
  100.         eOpenFail = 20;
  101.  
  102. { QuickDraw text styles }
  103.         kQDBold = 1;
  104.         kQDItalic = 2;
  105.         kQDUnderline = 4;
  106.         kQDOutline = 8;
  107.         kQDShadow = 16;
  108.  
  109.     TYPE
  110.     {A DocumentRecord contains the WindowRecord for one of our document windows,}
  111. {     as well as the TEHandle for the text we are editing. We have added fields to}
  112. {     hold the ControlHandles to the vertical and horizontal scrollbars and to hold}
  113. {     the address of the default clikLoop that gets attached to a TERec when you call}
  114. {     TEAutoView. Other document fields can be added to this record as needed. For}
  115. {     a similar example, see how the Window Manager and Dialog Manager add fields}
  116. {     after the GrafPort.}
  117.         DocumentRecord = RECORD
  118.                 docWindow: WindowRecord;
  119.                 docTE: TEHandle;
  120.                 docVScroll: ControlHandle;
  121.                 docHScroll: ControlHandle;
  122.                 docClik: ProcPtr;
  123.                 fileName: Str255;
  124.                 WDRefNum: INTEGER;
  125.                 myPrintRec: THPrint;
  126.             END;
  127.         DocumentPeek = ^DocumentRecord;
  128.  
  129. (*----------------------------- Variables -----------------------------------*)
  130.     VAR
  131.     (*-------- External Variables ---------*)
  132.         gMyFileType: ARRAY[1..kNativeTypes] OF TransDescribe;
  133.         gFilterSelected: INTEGER;
  134.         gTheReply: SFReply;
  135.         gInitWndSize: Point;        (*initial window size for saved files*)
  136.         gTheActiveWindow: WindowPtr;    (*pointer to front window*)
  137.         WATCH: CursHandle;
  138.  
  139.     (*-------- text ---------*)
  140.         gTextH: TEHandle;        (*handle to text in front window*)
  141.         gSelStart: INTEGER;        (*start of initial selection range*)
  142.         gSelEnd: INTEGER;        (*end of initial selection range*)
  143.         gIBeamHdl: CursHandle;    (*handle to the I-beam cursor image*)
  144.  
  145.     (* The following globals are to support XTND import/export *)
  146.         gShowTextRuns: Boolean;
  147.         gBeforeDivider: Boolean;
  148.         gXTNDAvail: Boolean;
  149.  
  150.     (*-------- Global Variables ---------*)
  151.         gImportPB: ImportParmBlock;
  152.         gPictImportPB: PictImportParmBlk;
  153.         exportPB: ExportParmBlock;
  154.         gExportTranslator, gImportTranslator: TransProcPtr;
  155.         gParafmts: ARRAY[1..9] OF Fixed;
  156.         gTabs: tabspecArray;
  157.         gFNMarker: PACKED ARRAY[1..10] OF Byte;
  158.         gNow: LongInt;
  159.         gNumDocuments, gFootnoteCount: INTEGER;
  160.         gFNStoryCount: INTEGER;
  161.         gPictCount: INTEGER;
  162.         gExportTextHandle: Handle;
  163.  
  164.         gExportTextLength: LongInt;
  165.         gExportError: INTEGER;
  166.         gExportRefNum: INTEGER;
  167.         gExportTxtFace: INTEGER;
  168.         gExportTxtSize: INTEGER;
  169.         gExportTxtFont: INTEGER;
  170.         gExportTxtColor: Byte;
  171.         gExportTxtJust: INTEGER;
  172.         Load_stored: INTEGER;
  173.         Save_stored: INTEGER;
  174.  
  175.  
  176. (*--------------------------- Routines in this file ----------------------------*)
  177.     PROCEDURE AlertUser (error, code: INTEGER);
  178.     PROCEDURE AdjustHV (isVert: BOOLEAN; control: ControlHandle; docTE: TEHandle; canRedraw: BOOLEAN);
  179.     PROCEDURE AdjustScrollValues (window: WindowPtr; canRedraw: BOOLEAN);
  180.     PROCEDURE GetTERect (window: WindowPtr; VAR teRect: Rect);
  181.     FUNCTION IsDAWindow (window: WindowPtr): BOOLEAN;
  182.     FUNCTION IsAppWindow (window: WindowPtr): BOOLEAN;
  183.     FUNCTION DoCloseWindow (window: WindowPtr): BOOLEAN;
  184.     PROCEDURE DoSave (saveAs: Boolean);
  185.     PROCEDURE DoOpen;
  186.     PROCEDURE DoNew;
  187.  
  188.  
  189. (* ========================================================================≠============≠============== *)
  190. IMPLEMENTATION
  191.  
  192.     CONST
  193. {QUICKDRAWSTYLES = ORD(bold) + ORD(italic) + ORD(underline) + ORD(outline) + ORD(shadow); }
  194.         QUICKDRAWSTYLES = 127;
  195.  
  196.  
  197. {$S Import}
  198.     FUNCTION IsDAWindow (window: WindowPtr): BOOLEAN;
  199. {  Check if a window belongs to a desk accessory. }
  200.     BEGIN { IsDAWindow }
  201.         IF window = NIL THEN
  202.             IsDAWindow := FALSE
  203.         ELSE    { DA windows have negative windowKinds }
  204.             IsDAWindow := WindowPeek(window)^.windowKind < 0;
  205.     END; { IsDAWindow }
  206.  
  207.     FUNCTION IsAppWindow (window: WindowPtr): BOOLEAN;
  208. {  Check if a window belongs to the application. }
  209.     BEGIN { IsAppWindow }
  210.         IF window = NIL THEN
  211.             IsAppWindow := FALSE
  212.         ELSE    { application windows have non-negative windowKinds }
  213.             IsAppWindow := WindowPeek(window)^.windowKind >= 0;
  214.     END; { IsAppWindow }
  215.  
  216.     PROCEDURE AlertUser (error, code: INTEGER);
  217. {  Display an alert that tells the user an error occurred, then exit the program }
  218.         VAR
  219.             itemHit: INTEGER;
  220.             message, tempStr: Str255;
  221.  
  222.     BEGIN { AlertUser }
  223.         SetCursor(arrow);
  224.         GetIndString(message, kErrStrings, error);
  225.         IF code <> 0 THEN BEGIN
  226.             NumToString(code, tempStr);
  227.             tempStr := concat('error number ', tempStr);
  228.         END
  229.         ELSE
  230.             tempStr := '';
  231.         ParamText(message, tempStr, '', '');
  232.         itemHit := Alert(rUserAlert, NIL);
  233.     END; { AlertUser }
  234.  
  235.     PROCEDURE AdjustHV (isVert: BOOLEAN; control: ControlHandle; docTE: TEHandle; canRedraw: BOOLEAN);
  236.  
  237. {Calculate the new control maximum value and current value, whether it is the horizontal or}
  238. {vertical scrollbar. The vertical max is calculated by comparing the number of lines to the}
  239. {vertical size of the viewRect. The horizontal max is calculated by comparing the maximum document}
  240. {width to the width of the viewRect. The current values are set by comparing the offset between}
  241. {the view and destination rects. If necessary and we canRedraw, have the control be re-drawn by}
  242. {calling ShowControl.}
  243.  
  244. {TEStyleSample-vertical max originally used line by line calculations-lineheight was a}
  245. {constant value so it was easy to figure out what the range should be and pin the value}
  246. {within range. Now we need to use max and min values in pixels rather than in nlines}
  247.  
  248.         VAR
  249.             value, max: INTEGER;
  250.             oldValue, oldMax: INTEGER;
  251.  
  252.     BEGIN { AdjustHV }
  253.         oldValue := GetCtlValue(control);
  254.         oldMax := GetCtlMax(control);
  255.         IF isVert THEN BEGIN
  256.         { new for TEStyleSample }
  257.             max := (TEGetHeight(docTE^^.nLines, 0, docTE)) - (docTE^^.viewRect.bottom - docTE^^.viewRect.top);
  258.         END
  259.         ELSE
  260.             max := kMaxDocWidth - (docTE^^.viewRect.right - docTE^^.viewRect.left);
  261.  
  262.         IF max < 0 THEN
  263.             max := 0;            { check for negative values }
  264.         SetCtlMax(control, max);
  265.         IF isVert THEN
  266.             value := docTE^^.viewRect.top - docTE^^.destRect.top
  267.         ELSE
  268.             value := docTE^^.viewRect.left - docTE^^.destRect.left;
  269.         IF value < 0 THEN
  270.             value := 0
  271.         ELSE IF value > max THEN
  272.             value := max;        { pin the value to within range }
  273.         SetCtlValue(control, value);
  274.         IF canRedraw & ((max <> oldMax) | (value <> oldValue)) THEN
  275.             ShowControl(control);            { check to see if the control can be re-drawn }
  276.     END; { AdjustHV }
  277.  
  278.     PROCEDURE GetTERect (window: WindowPtr; VAR teRect: Rect);
  279. {   return a rectangle that is inset from the portRect by the size of}
  280. {    the scrollbars and a little extra margin. }
  281.     BEGIN { GetTERect }
  282.         teRect := window^.portRect;
  283.         InsetRect(teRect, kTextMargin, kTextMargin);        { adjust for margin }
  284.         teRect.bottom := teRect.bottom - kScrollbarAdjust;    { and for the scrollbars }
  285.         teRect.right := teRect.right - kScrollbarAdjust;
  286.     END; { GetTERect }
  287.  
  288.     FUNCTION DoCloseWindow (window: WindowPtr): BOOLEAN;
  289. {    Close a window. This handles desk accessory and application windows. }
  290.  
  291.     BEGIN { DoCloseWindow }
  292.         DoCloseWindow := TRUE;
  293.         IF IsDAWindow(window) THEN
  294.             CloseDeskAcc(WindowPeek(window)^.windowKind)
  295.         ELSE IF IsAppWindow(window) THEN BEGIN
  296.             WITH DocumentPeek(window)^ DO
  297.                 IF docTE <> NIL THEN
  298.                     TEDispose(docTE);
  299.             CloseWindow(window);
  300.             DisposPtr(Ptr(window));
  301.             gNumDocuments := gNumDocuments - 1;
  302.         END;
  303.     END; { DoCloseWindow }
  304.  
  305.     PROCEDURE AdjustScrollValues (window: WindowPtr; canRedraw: BOOLEAN);
  306.  
  307. {    Simply call the common adjust routine for the vertical and horizontal scrollbars. }
  308.  
  309.     BEGIN { AdjustScrollValues }
  310.         WITH DocumentPeek(window)^ DO BEGIN
  311.             AdjustHV(TRUE, docVScroll, docTE, canRedraw);
  312.             AdjustHV(FALSE, docHScroll, docTE, canRedraw);
  313.         END; { with }
  314.     END; { AdjustScrollValues }
  315.  
  316.     PROCEDURE AsmClikLoop;
  317.     EXTERNAL;
  318. {    A reference to our assembly language routine that gets attached to the clikLoop field of our TE record. }
  319.     PROCEDURE DoNew;
  320. {    Create a new document and window. }
  321.  
  322. {Minor changes from TESample--TEStylNew instead of TENew-makes certain fields in}
  323. {the edit record (lineHeight, txFont, and txFace) have value of -1 and alloctes new}
  324. {tables to hold style information}
  325.  
  326.         VAR
  327.             good, ignore: BOOLEAN;
  328.             storage: Ptr;
  329.             window: WindowPtr;
  330.             destRect, viewRect: Rect;
  331.  
  332.     BEGIN { DoNew }
  333.         storage := NewPtr(SIZEOF(DocumentRecord));
  334.         IF storage <> NIL THEN BEGIN
  335.             window := GetNewWindow(rDocWindow, storage, WindowPtr(-1));
  336.             IF window <> NIL THEN BEGIN
  337.                 gTheActiveWindow := window;
  338.                 gNumDocuments := gNumDocuments + 1;
  339.                 good := FALSE;
  340.                 SetPort(window);
  341.                 WITH window^, DocumentPeek(window)^ DO BEGIN
  342.                     GetTERect(window, viewRect);
  343.                     destRect := viewRect;
  344.                     destRect.right := destRect.left + kMaxDocWidth;
  345.                     docTE := TEStylNew(destRect, viewRect);
  346.                 { Use TEStylNew instead of TENew to initialize TERec correctly }
  347.                     IF docTE <> NIL THEN BEGIN
  348.                         good := TRUE;                {if TENew succeeded, we have a good document}
  349.                         TEAutoView(TRUE, docTE);
  350.                         docClik := docTE^^.clikLoop;
  351.                         docTE^^.clikLoop := @AsmClikLoop;
  352.                     END;
  353.                     IF good THEN BEGIN
  354.                         myPrintRec := THPrint(NewHandle(sizeof(TPrint)));
  355.                         IF myPrintRec <> NIL THEN BEGIN
  356.                             PrOpen;
  357.                             PrintDefault(myPrintRec); (* load in default settings *)
  358.                             PrClose;
  359.                         END
  360.                         ELSE
  361.                             myPrintRec := NIL;
  362.                     END;
  363.                     IF good THEN BEGIN
  364.                         docVScroll := GetNewControl(rVScroll, window);
  365.                         good := (docVScroll <> NIL);
  366.                     END; { if }
  367.                     IF good THEN BEGIN
  368.                         docHScroll := GetNewControl(rHScroll, window);
  369.                         good := (docHScroll <> NIL);
  370.                     END; { if }
  371.                     IF good THEN BEGIN
  372.                         AdjustScrollValues(window, FALSE);
  373.                         ShowWindow(window); { if the document is good, make the window visible }
  374.                     END
  375.                     ELSE BEGIN
  376.                         ignore := DoCloseWindow(window); { otherwise regret we ever created it... }
  377.                         AlertUser(eNoWindow, 0); { and tell user }
  378.                     END { if }
  379.                 END; { with }
  380.             END
  381.             ELSE
  382.                 DisposPtr(storage); { get rid of the storage if it is never used }
  383.         END; { if }
  384.     END; { DoNew }
  385.  
  386.  
  387. (* ------------------------------------------------------------------------+------------+-------------- *)
  388.     PROCEDURE RGBFromXTND (VAR rgb: RGBColor; colorcode: INTEGER);
  389.     BEGIN
  390.         CASE colorcode OF
  391.             0:    { WHITE }
  392.                 BEGIN
  393.                 rgb.red := 65535;
  394.                 rgb.green := 65535;
  395.                 rgb.blue := 65535
  396.             END;
  397.             1:    { BLACK }
  398.                 BEGIN
  399.                 rgb.red := 0;
  400.                 rgb.green := 0;
  401.                 rgb.blue := 0
  402.             END;
  403.             2:    { RED }
  404.                 BEGIN
  405.                 rgb.red := 65535;
  406.                 rgb.green := 0;
  407.                 rgb.blue := 0
  408.             END;
  409.             3:    { GREEN }
  410.                 BEGIN
  411.                 rgb.red := 0;
  412.                 rgb.blue := 0;
  413.                 rgb.green := 65535
  414.             END;
  415.             4:    { BLUE }
  416.                 BEGIN
  417.                 rgb.red := 0;
  418.                 rgb.green := 0;
  419.                 rgb.blue := 65535
  420.             END;
  421.             5:    { CYAN }
  422.                 BEGIN
  423.                 rgb.red := 0;
  424.                 rgb.green := 65535;
  425.                 rgb.blue := 65535
  426.             END;
  427.             6:    { MAGENTA }
  428.                 BEGIN
  429.                 rgb.red := 65535;
  430.                 rgb.blue := 65535;
  431.                 rgb.green := 0
  432.             END;
  433.             7:    { YELLOW }
  434.                 BEGIN
  435.                 rgb.red := 65535;
  436.                 rgb.green := 65535;
  437.                 rgb.blue := 0
  438.             END
  439.         END
  440.     END;
  441.  
  442.  
  443. (* ------------------------------------------------------------------------+------------+-------------- *)
  444.     FUNCTION RGBToXTND (theColor: RGBColor): INTEGER;
  445.     (* ColorMap contains the conversion from QuickDraw color to our color id *)
  446.         VAR
  447.             r, g, b: INTEGER;
  448.             colormap: ARRAY[0..7] OF INTEGER;
  449.     BEGIN
  450.  
  451.         colormap[0] := 1;
  452.         colormap[1] := 4;
  453.         colormap[2] := 3;
  454.         colormap[3] := 5;
  455.         colormap[4] := 2;
  456.         colormap[5] := 6;
  457.         colormap[6] := 7;
  458.         colormap[7] := 0;
  459.  
  460.         IF BAND(theColor.red, $8000) <> 0 THEN
  461.             r := 4
  462.         ELSE
  463.             r := 0;
  464.         IF BAND(theColor.green, $8000) <> 0 THEN
  465.             g := 2
  466.         ELSE
  467.             g := 0;
  468.         IF BAND(theColor.blue, $8000) <> 0 THEN
  469.             b := 1
  470.         ELSE
  471.             b := 0;
  472.         RGBToXTND := colormap[r + g + b];
  473.     END;
  474.  
  475.     FUNCTION GetStyleFrom (XTNDStyle: INTEGER): Style;
  476.         VAR
  477.             newStyle: Style;
  478.     BEGIN
  479.         newStyle := [];        { Plain }
  480.  
  481.         IF BAND(XTNDStyle, kQDBold) <> 0 THEN
  482.             newStyle := newStyle + [bold];
  483.  
  484.         IF BAND(XTNDStyle, kQDItalic) <> 0 THEN
  485.             newStyle := newStyle + [italic];
  486.  
  487.         IF BAND(XTNDStyle, kQDUnderline) <> 0 THEN
  488.             newStyle := newStyle + [underline];
  489.  
  490.         IF BAND(XTNDStyle, kQDOutline) <> 0 THEN
  491.             newStyle := newStyle + [outline];
  492.  
  493.         IF BAND(XTNDStyle, kQDShadow) <> 0 THEN
  494.             newStyle := newStyle + [shadow];
  495.  
  496.         GetStyleFrom := newStyle;
  497.     END;
  498.  
  499. (* ========================================================================≠============≠============== *)
  500.     PROCEDURE ReadFile (pChosenOne: TransDescrPtr; theReply: SFReply);
  501.         VAR
  502.             window: WindowPtr;
  503.             dummyptr: Ptr;
  504.             TESlop: LONGINT;
  505.             pm: pictMiscHdl;
  506.             importPB: ImportParmBlock;
  507.             hfsPB: ParamBlockRec;
  508.             te: TEHandle;
  509.             Parafmt: ARRAY[0..8] OF Fixed;
  510.             Tabs: ARRAY[0..19] OF tabspec;
  511.             MinusOne: Point;
  512.             tempRect: Rect;
  513.             Marker: ARRAY[0..9] OF Byte;
  514.             fnum, resfnum, fserr: INTEGER;
  515.             aPtr: IntegerPtr;
  516.             count, textrun: LONGINT;
  517.             newStyle: TextStyle;
  518.             Buffer, theNumber: Str255;
  519.             now: LONGINT;
  520.             dummy: OSErr;
  521.             handleLocked: Boolean;
  522.             tempStyle: TEStyleHandle;
  523.             state: SignedByte;
  524.     BEGIN
  525.         window := FrontWindow;
  526.         TESlop := SIZEOF(TextStyle) + 500;
  527.         fnum := 0;
  528.         resfnum := 0;
  529.         textrun := 0;
  530.  
  531.         SetCursor(GetCursor(watchCursor)^^);
  532.         SetWTitle(window, theReply.fName);
  533.         fserr := XTNDLoadTranslator(pChosenOne, gImportTranslator);
  534.         IF fserr <> noErr THEN BEGIN
  535.             AlertUser(eTranslatorLoad, fserr);
  536.             EXIT(ReadFile);
  537.         END;
  538.         MinusOne.v := -1;
  539.         MinusOne.h := -1;
  540.         te := DocumentPeek(window)^.docTE;
  541.         importPB.TextBuffer := @Buffer;
  542.         importPB.result := noErr;
  543.         importPB.TextLength := 0;
  544.         importPB.TxtFace := 0;    { Plain }
  545.         importPB.TxtSize := 0;
  546.         importPB.TxtFont := helvetica;
  547.         importPB.TxtColor := 0;
  548.         importPB.TxtJust := 0;    { Left }
  549.         importPB.ParaFmts := @Parafmt;
  550.         importPB.Tabs := @Tabs;
  551.         importPB.NumCols := 1;
  552.         importPB.CurrentStory := mainStory;
  553.         importPB.MiscData := 0;
  554.         importPB.StoryHeight := 0;
  555.         importPB.DecimalChar := '.';
  556.         importPB.AutoHyphenate := TRUE;
  557.         importPB.PrintRecord := NIL;
  558.         importPB.StartPageNum := 1;
  559.         importPB.StartFootnoteNum := 1;
  560.         Marker[0] := 0;
  561.         importPB.FootnoteText := @Marker;
  562.         importPB.RulerShowing := TRUE;
  563.         importPB.DoubleSided := FALSE;
  564.         importPB.TitlePage := FALSE;
  565.         importPB.Endnotes := FALSE;
  566.         importPB.ShowInvisibles := FALSE;
  567.         importPB.ShowPageGuides := TRUE;
  568.         importPB.ShowPictures := TRUE;
  569.         importPB.AutoFootnotes := TRUE;
  570.         importPB.PagePoint := MinusOne;
  571.         importPB.DatePoint := MinusOne;
  572.         importPB.TimePoint := MinusOne;
  573.         importPB.SmartQuotes := TRUE;
  574.         importPB.FractCharWidths := FALSE;
  575.         importPB.HRes := 72;
  576.         importPB.VRes := 72;
  577.         importPB.TheReply := theReply;
  578.         importPB.ThisTranslator := pChosenOne^;
  579.         IF OpenRFPerm(theReply.fName, theReply.vRefNum, fsRdPerm) = -1 THEN BEGIN
  580.             IF ResError <> eofErr THEN                { No resource fork found }
  581.                 BEGIN
  582.                 fserr := ResError;
  583.                 AlertUser(eFilterRead, fserr);
  584.                 dummy := XTNDReleaseTranslator(pChosenOne);
  585.                 EXIT(ReadFile)
  586.             END;
  587.             UseResFile(pChosenOne^.ResRefNum);        { For translators expecting to be the current resource file }
  588.         END
  589.         ELSE        { If there is a resource fork for this file, read the resources }
  590.             BEGIN
  591.             resfnum := CurResFile;
  592.             importPB.RefNum := resfnum;
  593.             importPB.Directive := ImportGetResources;
  594.             XTNDCallTranslator(@importPB, gImportTranslator);
  595.             IF importPB.result <> noErr THEN BEGIN
  596.                 AlertUser(eFilterRead, importPB.result);
  597.                 CloseResFile(resfnum);
  598.                 dummy := XTNDReleaseTranslator(pChosenOne);
  599.                 EXIT(ReadFile)
  600.             END
  601.         END;
  602.  
  603.     { Open the file read only }
  604.         fserr := 0;
  605.         hfsPB.ioNamePtr := @theReply.fName;
  606.         hfsPB.ioVRefNum := theReply.vRefNum;
  607.         hfsPB.ioVersNum := 1;
  608.         hfsPB.ioPermssn := fsRdPerm;
  609.         hfsPB.ioMisc := Ptr(0);
  610.         fserr := PBOpen(@hfsPB, FALSE);
  611.         IF fserr <> noErr THEN BEGIN
  612.             AlertUser(eFilterInit, fserr);
  613.             CloseResFile(resfnum);
  614.             dummy := XTNDReleaseTranslator(pChosenOne);
  615.             EXIT(ReadFile)
  616.         END;
  617.         fnum := hfsPB.ioRefNum;
  618.         importPB.RefNum := hfsPB.ioRefNum;
  619.         importPB.Directive := ImportInitAll;
  620.         XTNDCallTranslator(@importPB, gImportTranslator);
  621.  
  622.     { After completing the initialization, check for an error.  If none, proceed. }
  623.         IF importPB.result <> noErr THEN BEGIN
  624.             AlertUser(eFilterInit, importPB.result);
  625.             CloseResFile(resfnum);
  626.             dummy := XTNDReleaseTranslator(pChosenOne);
  627.             EXIT(ReadFile)
  628.         END;
  629.  
  630.     { STAGE ONE - just read in the TEXT of the file.  Ignore pictures }
  631.  
  632.     { Set starting place to be the MAIN body of text. }
  633.         importPB.Directive := ImportInitMain;
  634.         importPB.CurrentStory := mainStory;
  635.         XTNDCallTranslator(@importPB, gImportTranslator);
  636.         IF importPB.result = noErr THEN BEGIN
  637.  
  638.             SetRect(tempRect, 0, 0, 0, 0);
  639.             ClipRect(tempRect);                        { close clip rect so text will not be drawn }
  640.             GetDateTime(now);
  641.             WHILE textrun < 30000 DO BEGIN
  642.                 importPB.Directive := ImportGetText;
  643.                 XTNDCallTranslator(@importPB, gImportTranslator);
  644.  
  645.                 fserr := importPB.result;
  646.                 count := importPB.TextLength;
  647.  
  648.                 IF (fserr <> noErr) OR ((importPB.Directive = ImportAcknowledge) AND (count <= 0)) THEN
  649.                     LEAVE;
  650.                 IF (count = 1) THEN BEGIN
  651.                     IF (ORD(Buffer[0]) < 32) THEN        { Is it a special character? }
  652.                         CASE ORD(Buffer[0]) OF
  653.                             2,    { Page Number }
  654.                             3,    { Footnote reference }
  655.                             5,    { Footnote reference }
  656.                             6,    { Merge Break Char }
  657.                             9,    { Tab }
  658.                             11,    { Column Break }
  659.                             12,    { Page Break }
  660.                             31:    { Discretionary Hyphen }
  661.                                 count := 0;
  662.  
  663.                             4:    { Picture }
  664.                             { We have to dispose of the picture, even if we don't use it. }
  665.                                 BEGIN
  666.                                 pm := pictMiscHdl(importPB.MiscData);
  667.                                 DisposHandle(Handle(pm^^.ThePicture));
  668.                                 DisposHandle(Handle(pm));
  669.                                 count := 0
  670.                             END;
  671.  
  672.                             21,    { Short Date }
  673.                             22,    { Abbrev Date }
  674.                             23,    { Long date }
  675.                             24,    { Abbrev + day Date }
  676.                             25:    { Long + day Date }
  677.                                 BEGIN
  678.                                 IF importPB.MiscData <> 0 THEN
  679.                                     IUDateString(importPB.MiscData, shortDate, theNumber)
  680.                                 ELSE
  681.                                     IUDateString(now, shortDate, theNumber);
  682.                                 count := ORD(theNumber[0]);
  683.                                 BlockMove(Ptr(ORD4(@theNumber) + 1), @Buffer, count);
  684.                             END;
  685.  
  686.                             26:    { Time }
  687.                                 BEGIN
  688.                                 IF importPB.MiscData <> 0 THEN
  689.                                     IUTimeString(importPB.MiscData, FALSE, theNumber)
  690.                                 ELSE
  691.                                     IUTimeString(now, FALSE, theNumber);
  692.                                 count := ORD(theNumber[0]);
  693.                                 BlockMove(Ptr(ORD4(@theNumber) + 1), @Buffer, count);
  694.                             END;
  695.  
  696.                             7:    { Hard Return }
  697.                                 Buffer[0] := CHR(13);
  698.                         END;
  699.                 END;
  700.  
  701.                 IF count <> 0 THEN BEGIN
  702.                 { Boy, is TextEdit buggy !  We need to see if there is enough memory to add the textrun }
  703.                     dummyptr := NewPtr(count + TESlop);
  704.                     IF dummyptr = NIL THEN
  705.                         LEAVE
  706.                     ELSE
  707.                         DisposPtr(dummyptr);
  708.  
  709.                     aPtr := IntegerPtr(@newStyle.tsFace);    { Fix a bug in text edit }
  710.                     aPtr^ := 0;
  711.  
  712.                     newStyle.tsFont := importPB.TxtFont;
  713.                     newStyle.tsFace := GetStyleFrom(importPB.TxtFace);
  714.                     newStyle.tsSize := importPB.TxtSize;
  715.                     RGBFromXTND(newStyle.tsColor, importPB.TxtColor);
  716.                     TESetStyle(doAll, newStyle, TRUE, te);
  717.  
  718.                 { Now add the number of characters to the text edit handle in this window }
  719.                     TEInsert(@Buffer, count, te);
  720.                     IF MemError <> noErr THEN
  721.                         LEAVE;
  722.  
  723.                     textrun := textrun + count;
  724.                 { NumToString(textrun, theNumber);        { Used for debugging.  Shows count in window title }
  725.                 { SetWTitle(window, theNumber); }
  726.                 END;
  727.             END; {while}
  728.  
  729.             importPB.directive := importCloseMain;
  730.             XTNDCallTranslator(@importPB, gImportTranslator);
  731.         END;
  732.  
  733.         importPB.directive := importCloseAll;
  734.         XTNDCallTranslator(@importPB, gImportTranslator);
  735.  
  736.  
  737.         TECalText(te);                                  { calc line starts in TERecord }
  738.         TESetSelect(textrun, textrun, te);                { Set insertion point }
  739.         AdjustScrollValues(window, TRUE);
  740.         SetRect(tempRect, -8000, -8000, 8000, 8000);
  741.         ClipRect(tempRect);                                { open clip rect so text will be drawn }
  742.  
  743.         IF resfnum <> 0 THEN
  744.             CloseResFile(resfnum);
  745.         dummy := FSClose(fnum);
  746.         dummy := XTNDReleaseTranslator(pChosenOne);
  747.     END;
  748.  
  749.  
  750. (* ------------------------------------------------------------------------+------------+-------------- *)
  751.     FUNCTION ReadPlainTextFile (theReply: SFReply; hTE: TEHandle): OSErr;
  752. (*    Inserts the text from the TEXT document specified by the Standard}
  753. {File reply record theReply into the TextEdit record specified by hTE. The}
  754. {file is assumed to be initially closed. The file is opened, the text is}
  755. {inserted at the current insertion point, the window’s scrollbars are}
  756. {adjusted, and the file is closed. The user is alerted if an error occured.}
  757. {    Note: This version of ReadPlainTextFile() is very simplistic. It reads}
  758. {the text into a block in the heap in one shot, then inserts it into the TE}
  759. {record. If the free memory isn’t at least twice the size of the text file}
  760. {ReadPlainTextFile() will fail. It also does not check the current size of}
  761. {the text of the TE record to guard against overflow. And it assumes that}
  762. {the specified file actually exists (which it may not if the Standard File}
  763. {reply record was not actually filled in by a Standard File routine). *)
  764. (*    04.19.91 m_o *)
  765.         LABEL
  766.             86;
  767.         VAR
  768.             window: WindowPtr;
  769.             err, dummy: OSErr;
  770.             myPB: ParamBlockRec;
  771.             hTx: Handle;
  772.     BEGIN
  773.         window := FrontWindow;
  774.         SetCursor(GetCursor(watchCursor)^^);
  775.         SetWTitle(window, theReply.fName);
  776.         hTx := NIL;
  777.     { open the text file… }
  778.         myPB.ioNamePtr := @theReply.fName;
  779.         myPB.ioVRefNum := theReply.vRefNum;
  780.         myPB.ioVersNum := 0;
  781.         myPB.ioPermssn := fsRdPerm;
  782.         myPB.ioMisc := NIL;
  783.         err := PBOpen(@myPB, FALSE);
  784.         IF err <> noErr THEN BEGIN
  785.             AlertUser(eOpenFail, err);
  786.             ReadPlainTextFile := err;
  787.             EXIT(ReadPlainTextFile)
  788.         END;
  789.     { find out how much text in the file… }
  790.         err := PBGetEOF(@myPB, FALSE);
  791.         IF err <> noErr THEN BEGIN
  792.             AlertUser(eOpenFail, err);
  793.             ReadPlainTextFile := err;
  794.             EXIT(ReadPlainTextFile)
  795.         END;
  796.     { get a buffer for the text… }
  797.         hTx := NewHandle(LONGINT(myPB.ioMisc));
  798.         IF hTx = NIL THEN BEGIN
  799.             AlertUser(eCreateFail, ResError);
  800.             GOTO 86
  801.         END;
  802.         MoveHHi(hTx);
  803.         HLock(hTx);
  804.     { read the file into the buffer… }
  805.         myPB.ioBuffer := hTx^;
  806.         myPB.ioReqCount := LONGINT(myPB.ioMisc);
  807.         myPB.ioPosMode := fsFromStart;
  808.         myPB.ioPosOffset := 0;
  809.         IF PBRead(@myPB, FALSE) = noErr THEN BEGIN
  810.         { insert text from buffer into TE record… }
  811.             TEInsert(hTx^, myPB.ioActCount, hTE);
  812.         { adjust window’s scrollbars… }
  813.             AdjustScrollValues(hTE^^.inPort, TRUE)
  814.         END;
  815. 86:
  816.         IF hTx <> NIL THEN
  817.             DisposHandle(hTx);
  818.         dummy := PBClose(@myPB, FALSE);
  819.         ReadPlainTextFile := err;
  820.     END;
  821.  
  822.     FUNCTION SetStyleFrom (oldStyle: Style): INTEGER;
  823.         VAR
  824.             newStyle: INTEGER;
  825.     BEGIN
  826.         newStyle := 0;        { Plain }
  827.  
  828.         IF bold IN oldStyle THEN
  829.             newStyle := newStyle + kQDBold;
  830.  
  831.         IF italic IN oldStyle THEN
  832.             newStyle := newStyle + kQDItalic;
  833.  
  834.         IF underline IN oldStyle THEN
  835.             newStyle := newStyle + kQDUnderline;
  836.  
  837.         IF outline IN oldStyle THEN
  838.             newStyle := newStyle + kQDOutline;
  839.  
  840.         IF shadow IN oldStyle THEN
  841.             newStyle := newStyle + kQDShadow;
  842.  
  843.         SetStyleFrom := newStyle;
  844.     END;
  845.  
  846. (* ------------------------------------------------------------------------+------------+-------------- *)
  847.     PROCEDURE SaveFile (pChosenOne: TransDescrPtr; theReply: SFReply);
  848.         TYPE
  849.             StyleRunPtr = ^StyleRun;
  850.         VAR
  851.             loop: LONGINT;
  852.             fserr, fnum: INTEGER;
  853.             Match: MatchInfo;
  854.             exportPB: ExportParmBlock;
  855.             runlength: LONGINT;
  856.             textbuffer: Handle;
  857.             textface, textsize, textfont, textjust, selStart, selEnd, myEnd: INTEGER;
  858.             textcolor: SignedByte;
  859.             Paragraph: ARRAY[1..9] OF Fixed;
  860.             tabs: ARRAY[1..20] OF tabspec;
  861.             MinusOne: Point;
  862.             tempRect: Rect;
  863.             te: TEHandle;
  864.             window: WindowPtr;
  865.             start, stylerun: LONGINT;
  866.             shndl: TEStyleHandle;
  867.             dummy: OSErr;
  868.             vRefNum: INTEGER;
  869.             sruns: StyleRunPtr;
  870.             theText: Handle;
  871.             length, offset, textLength: LONGINT;
  872.             Anentry: STElement;
  873.             styleruns: STPtr;
  874.             TextPtr: Ptr;
  875.             thestyles: STHandle;
  876.     BEGIN
  877.         fserr := 0;
  878.         window := FrontWindow;
  879.         te := DocumentPeek(window)^.docTE;
  880.     { In order to save the document, we have to parse our own document, and determine}
  881. {      where the paragraph and style runs start and end.  This is not a simple project in}
  882. {      text edit! }
  883.  
  884.     { First, let's load the Translator, just so we know we can! }
  885.         fserr := XTNDLoadTranslator(pChosenOne, gExportTranslator);
  886.         IF fserr <> noErr THEN BEGIN
  887.             AlertUser(eTranslatorLoad, fserr);
  888.             EXIT(SaveFile)
  889.         END;
  890.  
  891.     { Now, create the file so we can delete it.  (Takes care of PMSP problem) }
  892.         dummy := Create(theReply.fName, theReply.vRefNum, '????', '????');
  893.         fserr := FSDelete(theReply.fName, theReply.vRefNum);
  894.         IF fserr <> noErr THEN
  895.         { Explain we couldn't delete the file - probably a write protect error }
  896.             AlertUser(eDeleteFailed, fserr)
  897.         ELSE BEGIN
  898.             Match := pChosenOne^.Matches[0];
  899.             fserr := Create(theReply.fName, theReply.vRefNum, Match.DocCreator, Match.DocType);
  900.             IF fserr <> noErr THEN
  901.                 AlertUser(eCreateFail, fserr)
  902.             ELSE BEGIN
  903.                 fserr := FSOpen(theReply.fName, theReply.vRefNum, fnum);
  904.                 IF fserr <> noErr THEN
  905.                     AlertUser(eOpenFail, fserr)
  906.             END
  907.         END;
  908.         IF fserr <> noErr THEN BEGIN
  909.             dummy := XTNDReleaseTranslator(pChosenOne);
  910.             EXIT(SaveFile)
  911.         END;
  912.  
  913.         Paragraph[1] := 0;        { left indent offset }
  914.         Paragraph[2] := 0;        { first line indent offset }
  915.         Paragraph[3] := 0;        { right indent offset }
  916.         Paragraph[4] := 0;        { leading }
  917.         Paragraph[5] := 0;        { space before paragraph }
  918.         Paragraph[6] := 0;        { space after paragraph }
  919.         Paragraph[7] := -1;        { leading units (lines) }
  920.         Paragraph[8] := 0;        { space before units (points) }
  921.         Paragraph[9] := 0;        { space after units (points) }
  922.         FOR loop := 1 TO 20 DO
  923.             tabs[loop].TabIndent := -1;
  924.  
  925.     { Initialize the export Translator }
  926.         SetRect(tempRect, 0, 0, 0, 0);
  927.         MinusOne.v := -1;
  928.         MinusOne.h := -1;
  929.         textbuffer := NewHandle(0);
  930.         IF textbuffer = NIL THEN BEGIN
  931.             dummy := XTNDReleaseTranslator(pChosenOne);
  932.             dummy := FSClose(fnum);
  933.             EXIT(SaveFile)
  934.         END;
  935.  
  936.         exportPB.ThePicture := NIL;
  937.         exportPB.PictRect := tempRect;
  938.         exportPB.FootnoteOffset := 0;
  939.         exportPB.PagePoint := MinusOne;
  940.         exportPB.DatePoint := MinusOne;
  941.         exportPB.TimePoint := MinusOne;
  942.  
  943.         exportPB.TextBuffer := textbuffer;
  944.         exportPB.TextLength := @runlength;
  945.         exportPB.result := @fserr;
  946.         exportPB.RefNum := @fnum;
  947.         exportPB.TxtFace := @textface;
  948.         exportPB.TxtSize := @textsize;
  949.         exportPB.TxtFont := @textfont;
  950.         exportPB.TxtColor := @textcolor;
  951.         exportPB.TxtJust := @textjust;
  952.         exportPB.ParaFmts := @Paragraph;
  953.         exportPB.Tabs := @tabs;
  954.         exportPB.FootnoteText := NIL;
  955.  
  956.         exportPB.topMargin := $00480000;        { 1 inch margin }
  957.         exportPB.bottomMargin := $00480000;    { 1 inch margin }
  958.         exportPB.leftMargin := $00480000;        { 1 inch margin }
  959.         exportPB.rightMargin := $00480000;    { 1 inch margin }
  960.         exportPB.Gutter := $000C0000;    { 12 point column gap }
  961.         exportPB.NumCols := 1;
  962.         exportPB.StartPageNum := 1;
  963.         exportPB.StartFootnoteNum := 1;
  964.         exportPB.CurrentStory := mainStory;
  965.         exportPB.RulerShowing := TRUE;
  966.         exportPB.DoubleSided := FALSE;
  967.         exportPB.TitlePage := FALSE;
  968.         exportPB.Endnotes := FALSE;
  969.         exportPB.ShowInvisibles := TRUE;
  970.         exportPB.ShowPageGuides := TRUE;
  971.         exportPB.ShowPictures := TRUE;
  972.         exportPB.AutoFootnotes := TRUE;
  973.         exportPB.SmartQuotes := TRUE;
  974.         exportPB.FractCharWidths := TRUE;
  975.         exportPB.HRes := 72;
  976.         exportPB.VRes := 72;
  977.         exportPB.WindowRect := tempRect;
  978.  
  979.         exportPB.HeaderStatus := 0;
  980.         exportPB.FooterStatus := 0;
  981.         myEnd := te^^.teLength;
  982.         exportPB.TotalCharCount := te^^.teLength;
  983.         exportPB.FootnotesExist := FALSE;
  984.  
  985.         exportPB.TheReply := theReply;
  986.         exportPB.ThisTranslator := pChosenOne^;
  987.  
  988.         selStart := te^^.selStart;
  989.         selEnd := te^^.selEnd;
  990.         SetRect(tempRect, 0, 0, 0, 0);
  991.         ClipRect(tempRect);                            { close clip rect so text will not be drawn }
  992.  
  993.         PrOpen;
  994.         IF PrError = noErr THEN BEGIN
  995.             exportPB.PrintRecord := THPrint(NewHandle(SIZEOF(TPrint)));
  996.             IF exportPB.PrintRecord <> NIL THEN BEGIN
  997.                 PrintDefault(exportPB.PrintRecord);
  998.                 IF PrValidate(exportPB.printRecord) THEN
  999.                 { who cares? }
  1000.                     ;
  1001.             END;
  1002.             PrClose;
  1003.         END
  1004.         ELSE
  1005.             exportPB.PrintRecord := NIL;
  1006.  
  1007.         exportPB.Directive := ExportInitAll;
  1008.         XTNDCallTranslator(@exportPB, gExportTranslator);
  1009.  
  1010.     { OK - let's open the main story }
  1011.  
  1012.         exportPB.Directive := ExportOpenMain;
  1013.         exportPB.CurrentStory := mainStory;
  1014.         XTNDCallTranslator(@exportPB, gExportTranslator);
  1015.  
  1016.         shndl := GetStylHandle(te);        { There may not be _any_ style runs. }
  1017.         IF shndl <> NIL THEN BEGIN
  1018.             theText := Handle(TEGetText(te));
  1019.             textLength := te^^.teLength;
  1020.             HLock(theText);
  1021.             TextPtr := theText^;
  1022.             HLock(Handle(shndl));
  1023.             sruns := @shndl^^.runs;
  1024.             thestyles := shndl^^.styleTab;
  1025.             HLock(Handle(thestyles));
  1026.             styleruns := thestyles^;
  1027.             FOR stylerun := 0 TO shndl^^.nRuns - 1 DO BEGIN
  1028.                 start := sruns^.startChar;
  1029.                 length := StyleRunPtr(ORD4(sruns) + SIZEOF(StyleRun))^.startChar - start;
  1030.                 IF length + start > textLength THEN
  1031.                     length := textLength - start;
  1032.                 offset := 0;
  1033.                 runlength := 0;
  1034.             { Find the associated style entry }
  1035.                 Anentry := styleruns^[sruns^.styleIndex];
  1036.                 IF Anentry.stSize = 0 THEN
  1037.                     Anentry.stSize := GetDefFontSize;
  1038.                 textface := SetStyleFrom(Anentry.stFace);
  1039.                 textsize := Anentry.stSize * 4;        { multiply by four to simulate MacWrite II font size }
  1040.                 textcolor := SignedByte(RGBToXTND(Anentry.stColor));
  1041.                 textfont := Anentry.stFont;
  1042.                 textjust := 0;    { left; }
  1043.                 WHILE offset + runlength < length DO BEGIN
  1044.                     WHILE offset + runlength < length DO BEGIN
  1045.                         IF Ptr(ORD4(TextPtr) + runlength)^ = 13 THEN BEGIN
  1046.                             runlength := runlength + 1;
  1047.                             LEAVE
  1048.                         END;
  1049.                         runlength := runlength + 1
  1050.                     END;
  1051.                 { Send runlength characters, starting at start + offset }
  1052.                     SetHandleSize(textbuffer, runlength);
  1053.                 { check to see if this fails }
  1054.                     BlockMove(TextPtr, textbuffer^, runlength);
  1055.                     exportPB.Directive := ExportWriteText;
  1056.                     XTNDCallTranslator(@exportPB, gExportTranslator);
  1057.                     TextPtr := Ptr(ORD4(TextPtr) + runlength);
  1058.                     TESetSelect(start + offset, start + offset + runlength, te);
  1059.                     offset := offset + runlength;
  1060.                     runlength := 0;
  1061.                 END;
  1062.                 sruns := StyleRunPtr(ORD4(sruns) + SIZEOF(StyleRun))
  1063.             END;
  1064.  
  1065.             exportPB.Directive := ExportCloseMain;
  1066.             XTNDCallTranslator(@exportPB, gExportTranslator);
  1067.         END;
  1068.  
  1069.         exportPB.Directive := ExportCloseAll;
  1070.         XTNDCallTranslator(@exportPB, gExportTranslator);
  1071.  
  1072.         IF exportPB.PrintRecord <> NIL THEN
  1073.             DisposHandle(Handle(exportPB.PrintRecord));
  1074.         dummy := FSClose(fnum);
  1075.  
  1076.     { Write resource fork now. }
  1077.         dummy := GetVol(NIL, vRefNum);
  1078.         dummy := SetVol(NIL, theReply.vRefNum);
  1079.         CreateResFile(theReply.fName);
  1080.         fnum := OpenResFile(theReply.fName);
  1081.         exportPB.Directive := ExportWriteResources;
  1082.         XTNDCallTranslator(@exportPB, gExportTranslator);
  1083.         CloseResFile(fnum);
  1084.         dummy := SetVol(NIL, vRefNum);
  1085.  
  1086.         dummy := XTNDReleaseTranslator(pChosenOne);
  1087.  
  1088.         TESetSelect(selStart, selEnd, te);            { Set insertion point }
  1089.         SetRect(tempRect, -8000, -8000, 8000, 8000);
  1090.         ClipRect(tempRect);                            { open clip rect so text will be drawn }
  1091.     END;
  1092.  
  1093.  
  1094. (* ------------------------------------------------------------------------+------------+-------------- *)
  1095.     FUNCTION SavePlainTextFile (fileName: Str255; vRefNum: INTEGER; dirID: LONGINT; hTE: TEHandle; saveAll: BOOLEAN): OSErr;
  1096. (*    Saves the text from the TextEdit record specified by hTE to the file}
  1097. {having the name fileName on the volume specified by vRefNum and in the}
  1098. {directory specified by dirID. The file is assumed to be initially closed.}
  1099. {It is opened, the text written out (replacing any previous contents of the}
  1100. {file), and the file is closed. If saveAll is TRUE the entire text of the}
  1101. {edit record is written out, otherwise only the text within the current}
  1102. {selection is saved. The user is alerted if an error occured. *)
  1103. (*    04.19.91 m_o *)
  1104.         LABEL
  1105.             86;
  1106.         VAR
  1107.             err, dummy: OSErr;
  1108.             mfb: SignedByte;
  1109.             myHPB: HParamBlockRec;
  1110.     BEGIN
  1111.     { open the file… }
  1112.         myHPB.ioNamePtr := @fileName;
  1113.         myHPB.ioVRefNum := vRefNum;
  1114.         myHPB.ioVersNum := 0;
  1115.         myHPB.ioPermssn := fsRdWrPerm;
  1116.         myHPB.ioMisc := NIL;
  1117.         myHPB.ioDirID := dirID;
  1118.         err := PBHOpen(@myHPB, FALSE);
  1119.         IF err <> noErr THEN BEGIN
  1120.             AlertUser(eOpenFail, err);
  1121.             SavePlainTextFile := err;
  1122.             EXIT(SavePlainTextFile)
  1123.         END;
  1124.     { reset eof of file to zero… }
  1125.         myHPB.ioMisc := Ptr(0);
  1126.         err := PBSetEOF(ParmBlkPtr(@myHPB), FALSE);
  1127.         IF err <> noErr THEN BEGIN
  1128.             AlertUser(eOpenFail, err);
  1129.             GOTO 86
  1130.         END;
  1131.     { temporarily lock down text block of TE record;}
  1132. {      this is paranoia since PBWrite() is not supposed to move/purge memory… }
  1133.         MoveHHi(hTE^^.hText);
  1134.         mfb := HGetState(hTE^^.hText);
  1135.         HLock(hTE^^.hText);
  1136.     { write out TE text to file & reset lock-state of text block… }
  1137.         IF saveAll = TRUE THEN BEGIN
  1138.             myHPB.ioBuffer := hTE^^.hText^;
  1139.             myHPB.ioReqCount := hTE^^.teLength
  1140.         END
  1141.         ELSE BEGIN
  1142.             myHPB.ioBuffer := Ptr(ORD4(hTE^^.hText^) + hTE^^.selStart);
  1143.             myHPB.ioReqCount := hTE^^.selEnd - hTE^^.selStart
  1144.         END;
  1145.         myHPB.ioPosMode := fsFromStart;
  1146.         myHPB.ioPosOffset := 0;
  1147.         err := PBWrite(ParmBlkPtr(@myHPB), FALSE);
  1148.         HSetState(hTE^^.hText, mfb);
  1149. 86:
  1150.         dummy := PBClose(ParmBlkPtr(@myHPB), FALSE);
  1151.         SavePlainTextFile := err;
  1152.     END;
  1153.  
  1154.  
  1155. (* ------------------------------------------------------------------------+------------+-------------- *)
  1156.     FUNCTION SaveNewPlainTextFile (theReply: SFReply; fileType: OSType; hTE: TEHandle; saveAll: BOOLEAN; VAR vRefNum: INTEGER; VAR dirID: LONGINT): OSErr;
  1157. (*    Creates a new file and saves the text from the TextEdit record }
  1158. {specified by hTE. The file is created with a creator of 'XTND' and filetype}
  1159. {fileType. The name and location of the file is specified by the Standard}
  1160. {File reply record *pTheReply. The directory ID and real volume reference}
  1161. {number of the specified location are returned through the VAR parameters}
  1162. {vRefNum and dDirID. Any existing file is first deleted. The file is closed}
  1163. {after saving its contents. If saveAll is TRUE the entire text of the edit}
  1164. {record is written out, otherwise only the text within the current selection}
  1165. {is saved. If an error occured the user is alerted and the file is deleted. *)
  1166. (*    04.19.91 m_o *)
  1167.         LABEL
  1168.             86;
  1169.         VAR
  1170.             err, dummy: OSErr;
  1171.             myWDPB: WDPBRec;
  1172.             myHPB: HParamBlockRec;
  1173.     BEGIN
  1174.     { get the dirID and real vRefNum… }
  1175.         myWDPB.ioNamePtr := NIL;
  1176.         myWDPB.ioVRefNum := theReply.vRefNum;
  1177.         myWDPB.ioWDIndex := 0;
  1178.         myWDPB.ioWDProcID := 0;
  1179.         err := PBGetWDInfo(@myWDPB, FALSE);
  1180.         IF err <> noErr THEN BEGIN
  1181.             AlertUser(eCreateFail, err);
  1182.             SaveNewPlainTextFile := err;
  1183.             EXIT(SaveNewPlainTextFile)
  1184.         END;
  1185.     { delete file (if it already exists)… }
  1186.         myHPB.ioNamePtr := @theReply.fName;
  1187.         vRefNum := myWDPB.ioWDVRefNum;
  1188.         myHPB.ioVRefNum := myWDPB.ioWDVRefNum;
  1189.         myHPB.ioVersNum := 0;
  1190.         dirID := myWDPB.ioWDDirID;
  1191.         myHPB.ioDirID := myWDPB.ioWDDirID;
  1192.         err := PBHDelete(@myHPB, FALSE);
  1193.         IF (err <> noErr) & (err <> fnfErr) THEN BEGIN
  1194.         { …possibly locked or busy, or who knows what }
  1195.             AlertUser(eDeleteFailed, err);
  1196.             SaveNewPlainTextFile := err;
  1197.             EXIT(SaveNewPlainTextFile)
  1198.         END;
  1199.     { create the file… }
  1200.         err := PBHCreate(@myHPB, FALSE);
  1201.         IF err <> noErr THEN BEGIN
  1202.             AlertUser(eCreateFail, err);
  1203.             SaveNewPlainTextFile := err;
  1204.             EXIT(SaveNewPlainTextFile)
  1205.         END;
  1206.     { write text to file… }
  1207.         err := SavePlainTextFile(theReply.fName, myWDPB.ioWDVRefNum, myWDPB.ioWDDirID, hTE, saveAll);
  1208.         IF err <> noErr THEN
  1209.             GOTO 86;
  1210.     { set filetype & creator information… }
  1211.         myHPB.ioFDirIndex := 0;
  1212.         err := PBHGetFInfo(@myHPB, FALSE);
  1213.         IF err = noErr THEN BEGIN
  1214.             myHPB.ioDirID := myWDPB.ioWDDirID;
  1215.             myHPB.ioFlFndrInfo.fdType := fileType;
  1216.             myHPB.ioFlFndrInfo.fdCreator := 'XTND';
  1217.             err := PBHSetFInfo(@myHPB, FALSE)
  1218.         END;
  1219. 86:
  1220.         IF err <> noErr THEN BEGIN
  1221.             AlertUser(eCreateFail, err);
  1222.             dummy := PBHDelete(@myHPB, FALSE)
  1223.         END
  1224.     END;
  1225.  
  1226.  
  1227. (* ========================================================================≠============≠============== *)
  1228.     PROCEDURE DoOpen;
  1229. (*    Handler for the open command. Prompts the user for the file to open. If}
  1230. {the user selects a file a new document window is created and the file is}
  1231. {read in.}
  1232. {    If the XTND Library was successfully initialized its XTNDGetFile()}
  1233. {routine is used to get the user’s document selection, otherwise the}
  1234. {Standard File SFGetFile() routine is used. *)
  1235. (*    /04.19.91 m_o *)
  1236.         VAR
  1237.             getIt: BOOLEAN;
  1238.             myReply: SFReply;
  1239.             myXSFPB: SFParamBlock;
  1240.             myPrompt, myBTitle: Str255;
  1241.             where: Point;
  1242.             myTypes: SFTypeList;
  1243.             numDocuments: INTEGER;
  1244.             topDoc: DocumentPeek;
  1245.             err: OSErr;
  1246.     BEGIN
  1247.         IF gXTNDAvail = TRUE THEN BEGIN
  1248.             myXSFPB.AllowFlags := allowText;
  1249.             myXSFPB.NumStandard := kNativeTypes;
  1250.             myXSFPB.Standard := @gMyFileType;
  1251.             myXSFPB.ioResult := 0;
  1252.             myXSFPB.FileReply := @myReply;
  1253.             myXSFPB.XTNDDlogHook := NIL;        { XTNDDlgHookProcPtr(MyDlg); }
  1254.             myXSFPB.CurrentMenuItem := Load_stored;
  1255.             myXSFPB.Where.v := 0;
  1256.             myXSFPB.Where.h := 0;
  1257.             myPrompt := 'Select a file to open';
  1258.             myXSFPB.Prompt := @myPrompt;
  1259.             myBTitle := 'Open';
  1260.             myXSFPB.ButtonTitle := @myBTitle;
  1261.             myXSFPB.DialogID := 0;
  1262.             myXSFPB.SFFilterProc := NIL;
  1263.             myXSFPB.ShowAllFiles := FALSE;
  1264.             myXSFPB.useMyTransList := FALSE;
  1265.             myXSFPB.myFileFilter := NIL;
  1266.             myXSFPB.Unused := 0;
  1267.             myReply.good := TRUE;
  1268.             getIt := XTNDGetFile(@myXSFPB);
  1269.             Load_stored := myXSFPB.CurrentMenuItem
  1270.         END
  1271.         ELSE BEGIN
  1272.             where.v := $40;
  1273.             where.h := $40;
  1274.             myTypes[1] := 'TEXT';
  1275.             SFGetFile(where, '', NIL, 1, myTypes, NIL, myReply);
  1276.             getIt := myReply.good
  1277.         END;
  1278.         IF getIt = TRUE THEN BEGIN
  1279.             numDocuments := gNumDocuments;
  1280.             DoNew;
  1281.             IF numDocuments <> gNumDocuments THEN    { Did we open a new window? }
  1282.                 IF (gXTNDAvail = TRUE) & (myXSFPB.chosenTranslator > myXSFPB.NumStandard) THEN
  1283.                     ReadFile(myXSFPB.theChosenTranslator, myReply)    { Read the file in using XTND. }
  1284.                 ELSE BEGIN
  1285.                 { Use the appropriate internally supported method of reading the file.}
  1286. {                  While our application claims to support three (kNativeTypes) formats, they are}
  1287. {                  all actually simple TEXT documents. }
  1288.                     topDoc := DocumentPeek(FrontWindow);
  1289.                     err := ReadPlainTextFile(myReply, topDoc^.docTE)
  1290.                 END;
  1291.         END
  1292.     END;
  1293.  
  1294.  
  1295. (* ------------------------------------------------------------------------+------------+-------------- *)
  1296.     PROCEDURE DoSave (saveAs: BOOLEAN);
  1297. (*    Handler for the Save and Save As commands. The parameter saveAs}
  1298. {specifies the Save As command when it is TRUE.}
  1299. {    When handling a Save As command the user is prompted for a filename and}
  1300. {location to save the document. The window title of the frontmost window is}
  1301. {used as a default filename. If the user specifies a filename and location}
  1302. {the file is created (deleting any previously existing one) and the contents}
  1303. {of the frontmost document window are written to it.}
  1304. {    If the XTND Library was successfully initialized its XTNDPutFile()}
  1305. {routine is used to get the user’s document selection, otherwise the}
  1306. {Standard File SFGetFile() routine is used.}
  1307. {    Handling of the Save command is currently not implemented. If DoSave()}
  1308. {is called with saveAs FALSE it will simply beep. *)
  1309. (*    /04.19.91 m_o *)
  1310.         VAR
  1311.             wTitle: Str255;
  1312.             putIt: BOOLEAN;
  1313.             myReply: SFReply;
  1314.             myXSFPB: SFParamBlock;
  1315.             window: WindowPtr;
  1316.             myPrompt, myBTitle: Str255;
  1317.             where: Point;
  1318.             vRefNum: INTEGER;
  1319.             dirID: LONGINT;
  1320.             err: OSErr;
  1321.     BEGIN
  1322.         window := FrontWindow;
  1323.         GetWTitle(window, wTitle);
  1324.         IF saveAs = FALSE THEN BEGIN
  1325.         { Handle a simple Save routine here. }
  1326.             SysBeep(1);
  1327.             EXIT(DoSave)
  1328.         END;
  1329.         IF gXTNDAvail = TRUE THEN BEGIN
  1330.             myXSFPB.AllowFlags := allowText + allowExport;
  1331.             myXSFPB.NumStandard := kNativeTypes;
  1332.             myXSFPB.Standard := @gMyFileType;
  1333.             myXSFPB.ioResult := 0;
  1334.             myXSFPB.FileReply := @myReply;
  1335.             myXSFPB.ApplicNativeType := 'TEXT';
  1336.             myXSFPB.XTNDDlogHook := NIL;        { XTNDDlgHookProcPtr(MyDlg); }
  1337.             myXSFPB.CurrentSaveItem := Save_stored;
  1338.             myXSFPB.Where.v := 0;
  1339.             myXSFPB.Where.h := 0;
  1340.             myPrompt := 'Export File';
  1341.             myXSFPB.Prompt := @myPrompt;
  1342.             myBTitle := 'Save';
  1343.             myXSFPB.ButtonTitle := @myBTitle;
  1344.             myXSFPB.OrigName := @wTitle;
  1345.             myXSFPB.DialogID := 0;
  1346.             myXSFPB.SFFilterProc := NIL;
  1347.             myXSFPB.useMyTransList := FALSE;
  1348.             myXSFPB.myFileFilter := NIL;
  1349.             myXSFPB.Unused := 0;
  1350.             myReply.good := TRUE;
  1351.             putIt := XTNDPutFile(@myXSFPB);
  1352.             Save_stored := myXSFPB.CurrentSaveItem
  1353.         END
  1354.         ELSE BEGIN
  1355.             where.v := $40;
  1356.             where.h := $40;
  1357.             myPrompt := 'Save document as:';
  1358.             SFPutFile(where, myPrompt, wTitle, NIL, myReply);
  1359.             putIt := myReply.good
  1360.         END;
  1361.         IF putIt = TRUE THEN
  1362.             IF (gXTNDAvail = TRUE) & (myXSFPB.chosenTranslator > myXSFPB.NumStandard) THEN
  1363.                 SaveFile(myXSFPB.theChosenTranslator, myReply)    { Save the file using XTND. }
  1364.             ELSE
  1365.             { Use the appropriate internally supported method of saving the file.}
  1366. {              While our application claims to support three (kNativeTypes) formats, they are}
  1367. {              all actually simple TEXT documents. The only differentiation we make is}
  1368. {              the fileType we create the documents as. }
  1369.                 err := SaveNewPlainTextFile(myReply, 'TEXT', DocumentPeek(window)^.docTE, TRUE, vRefNum, dirID) {saveAll}
  1370.     END;
  1371.  
  1372.  
  1373. END.